package m2dl.ter.mirrorgame;

import java.util.ArrayList;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

public class Mirror extends Activity implements SensorEventListener {

	/** Gestures table handling */
	final String EXTRA_GESTURES = "gestures";

	/** Possible noise according to movement. */
	private final float NOISE = (float) 5.0;

	/** The unique handler instance of the TMCGame. */
	protected Handler myHandler = null;

	/** List of movements. */
	protected ArrayList<Integer> movements;

	/** Score Mirror game. */
	protected float scoreGame = 0;

	/** The file persistent path. */
	protected final String PREFS_SCORE = "file";

	/** The utilities to persist the data file. */
	protected SharedPreferences pref = null;
	protected SharedPreferences.Editor prefsEditor = null;

	/** The music handler. */
	protected MediaPlayer mediaPlayer = null;

	/** Text view, see the movements. */
	protected TextView viewNbMovements = null, seeX = null, seeY = null;

	/** The image of direction. */
	protected ImageView idirection;

	/** Handler time. */
	protected long actualTime, lastUpdate;

	/** Accuracy. */
	protected final float alpha = (float) 0.8;
	protected float[] gravity = new float[3];
	protected float[] linear_acceleration = new float[3];

	/** UTILS for accelerometer sensor. */
	private float deltaX, deltaY, mLastX, mLastY;
	private boolean mInitialized;
	private SensorManager mSensorManager;
	private Sensor mAccelerometer;

	private Runnable mUpdateTimeTask = new Runnable() {
		String theMovement = "ERROR";
		String textTempNbMove = null;

		public void run() {
			// Launch again the handler
			theMovement = getMovement();
			if (movements.size() > 1) {
				setImageResourceCorresponding(theMovement);
				idirection.setVisibility(View.VISIBLE);
				textTempNbMove = "Nb moves: " + movements.size() + "Move: "
						+ theMovement;
				viewNbMovements.setText(textTempNbMove);
			} else {
				persisteScore((int) scoreGame);
				Intent intent = new Intent(Mirror.this, MenuActivity.class);
				startActivity(intent);
				System.exit(RESULT_OK);
			}
			myHandler.postDelayed(this, 10);
		}
	};

	@SuppressLint("WorldReadableFiles")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mirror);

		// Manages movements coordinates
		Intent intent = getIntent();
		this.movements = new ArrayList<Integer>();
		if (intent.getIntegerArrayListExtra(EXTRA_GESTURES) != null) {
			this.movements.addAll(intent
					.getIntegerArrayListExtra(EXTRA_GESTURES));
		} else {
			this.movements.add(1);
			this.movements.add(2);
			this.movements.add(0);
			this.movements.add(3);
			this.movements.add(2);
			this.movements.add(1);
			this.movements.add(3);
			this.movements.add(0);
			this.movements.add(1);
			this.movements.add(3);
		}
		// Manages text view
		this.viewNbMovements = (TextView) findViewById(R.id.id_mirror_nbMovements);
		this.viewNbMovements.setBackgroundColor(Color.RED);
		this.seeX = (TextView) findViewById(R.id.id_mirror_x);
		this.seeY = (TextView) findViewById(R.id.id_mirror_y);

		// Initialize the current score
		this.pref = this.getSharedPreferences(PREFS_SCORE, MODE_WORLD_READABLE);
		this.prefsEditor = pref.edit();

		// Manages images
		this.idirection = (ImageView) findViewById(R.id.id_mirror_direction);

		// Initialization of the handler
		this.myHandler = new Handler();
		this.myHandler.postDelayed(mUpdateTimeTask, 100);

		// Manages the score
		String textNbMovement = "Nn movements : " + this.movements.size();
		this.viewNbMovements.setText(textNbMovement);
		this.scoreGame = 0;

		// Manages accelerometer
		this.mInitialized = false;
		this.mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
		this.mAccelerometer = this.mSensorManager
				.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		this.mSensorManager.registerListener(this, mAccelerometer,
				SensorManager.SENSOR_DELAY_NORMAL);

		// Manages the sound game
		this.mediaPlayer = MediaPlayer.create(getBaseContext(), R.raw.sound);
		this.mediaPlayer.setLooping(true);
		this.mediaPlayer.start();
	}

	protected void onResume() {
		super.onResume();
		this.mediaPlayer.start();
		this.mSensorManager.registerListener(this, mAccelerometer,
				SensorManager.SENSOR_DELAY_NORMAL);
	}

	protected void onPause() {
		super.onPause();
		this.mediaPlayer.stop();
		this.mSensorManager.unregisterListener(this);
	}

	/** Write the new score in order to persist it. */
	private void persisteScore(int score) {
		// Write the score
		if (pref.getInt("score", 0) < score) {
			prefsEditor.putInt("score", score);
		}
		prefsEditor.putInt("newScore", score);
		prefsEditor.commit();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.mirror, menu);
		return true;
	}

	@Override
	public void onStop() {
		super.onStop();
		this.mediaPlayer.stop();
		System.exit(RESULT_OK);
		this.finish();
	}

	// Manages the menu
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.mirror_quitter:
			finish();
			return true;
		}
		return false;
	}

	@Override
	public void onAccuracyChanged(Sensor arg0, int arg1) {
		// can be safely ignored for this demo
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		// Checks that the game is not ended
		if (this.movements.size() > 1) {
			// check sensor type
			if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
				// assign directions
				float x = event.values[0];
				float y = event.values[2];

				if (!mInitialized) {
					this.mLastX = x;
					this.mLastY = y;
					this.mInitialized = true;
					this.lastUpdate = 0;
					gravity[0] = 0;
					gravity[0] = 0;
					gravity[0] = 0;
				}

				gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
				gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
				gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

				linear_acceleration[0] = event.values[0] - gravity[0];
				linear_acceleration[1] = event.values[1] - gravity[1];
				linear_acceleration[2] = event.values[2] - gravity[2];

				this.seeX.setText(linear_acceleration[0] + " || "
						+ linear_acceleration[1] + " || "
						+ linear_acceleration[2]);
				this.deltaX = this.mLastX - x;
				this.deltaY = this.mLastY - y;
				this.mLastX = x;
				this.mLastY = y;
				this.actualTime = System.currentTimeMillis();

				if ((this.actualTime - this.lastUpdate) > 200) {
					this.lastUpdate = this.actualTime;
					// Implements the mirror hearth game
					if (this.movements.get(0) != null) {
						int typeOfMovement = this.movements.get(0);
						switch (typeOfMovement) {
						case 0: // UP
							if (Math.abs(linear_acceleration[1]) > NOISE) {
								verifyMovement();
							} else {
								this.scoreGame -= 10;
							}
							break;
						case 1: // DOWN
							if (Math.abs(linear_acceleration[1]) > NOISE) {
								verifyMovement();
							} else {
								this.scoreGame -= 10;
							}
							break;
						case 2: // LEFT
							if (Math.abs(linear_acceleration[0]) > NOISE) {
								verifyMovement();
							} else {
								this.scoreGame -= 10;
							}
							break;
						case 3: // RIGHT
							if (Math.abs(linear_acceleration[0]) < NOISE) {
								verifyMovement();
							} else {
								this.scoreGame -= 10;
							}
							break;
						default:
							System.out.println("MOUVEMENT PAS DEMANDEE !!!!!");
							break;
						}
					}
				}
			}
		}
	}

	/** Verify the movement. */
	private void verifyMovement() {
		((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(200);
		this.scoreGame += 200;
		revomeMovement();
		this.idirection.setVisibility(View.VISIBLE);
	}

	// Gets the image resource corresponding
	private void setImageResourceCorresponding(String typeOfMovement) {
		if (typeOfMovement.equals("UP")) {
			this.idirection.setImageResource(R.drawable.arrow_up);
		} else if (typeOfMovement.equals("DOWN")) {
			this.idirection.setImageResource(R.drawable.arrow_down);
		} else if (typeOfMovement.equals("LEFT")) {
			this.idirection.setImageResource(R.drawable.arrow_left);
		} else if (typeOfMovement.equals("RIGHT")) {
			this.idirection.setImageResource(R.drawable.arrow_right);
		}
	}

	/** Gets the current movement to do. */
	private String getMovement() {
		String result = null;

		if (this.movements.get(0) != null) {
			int typeOfMovement = this.movements.get(0);
			switch (typeOfMovement) {
			case 0:
				result = "UP";
				break;
			case 1:
				result = "DOWN";
				break;
			case 2:
				result = "LEFT";
				break;
			case 3:
				result = "RIGHT";
				break;
			default:
				result = "UP";
			}
		}
		return result;
	}

	/** Removes the first movement. */
	private void revomeMovement() {
		this.movements.remove(0);
	}
}
